/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 *
 * $Id: owndebug.h,v 1.24 2009-06-08 07:34:01 amaula Exp $
 *
 * \file
 * \brief Debug printing library
 * This library provides an printing and logging facility capable for source
 * tracking, timestamping, thread_identifying and lots more :)
 * \author Antti Maula <antti.maula@tkk.fi>
 */

#ifndef _OWNDEBUG_H_
#define _OWNDEBUG_H_

// Library version :)
#define OWNDEBUG_LIBRARY_VERSION 1100

#include <stdio.h>
#include <errno.h>

///////////////// Debug Modes /////////////////////////////////////////////////
/**
 * The time is reported hh:mm:ss (This format is exclusive with HH_MM_SS_MS bit)
 */
#define DEBUG_MODE_TIMESTAMP_HH_MM_SS         (1<<0)

/**
 * The time is reported hh:mm:ss.ms (This format is exclusive with HH_MM_SS_MS)
 */
#define DEBUG_MODE_TIMESTAMP_HH_MM_SS_MS      (1<<1)

/**
 * The time is reported seconds.microseconds since the ownDebug
 * library was initialized.
 */
#define DEBUG_MODE_TIMESTAMP_DELTA_SS_US      (1<<8)

/**
 * Different format specifiers. Select only one, if multiple are
 * selected the used format is the first match in the following order.
 */
#define DEBUG_MODE_FORMAT_FILE_FUNC_LINE      (1<<2)
#define DEBUG_MODE_FORMAT_FILE_FUNC           (1<<3)
#define DEBUG_MODE_FORMAT_FILE_LINE           (1<<4)

/**
 * Print linefeed after 'format' 
 */
#define DEBUG_MODE_FORMAT_MSG_ON_NEXT_LINE    (1<<5)

/**
 * Flush the printed line immediately (calls flush() for the file descriptor
 * after write, NOTE: This may have negative impact on performance!)
 */
#define DEBUG_MODE_IMMEDIATE_FLUSH            (1<<6)

/**
 * Print ThreadID along with the line
 */
#define DEBUG_MODE_FORMAT_PRINTTHREADID       (1<<7)

/**
 * Guess ;)
 */
#define DEBUG_MODE_FORMAT_AALTONAATTORI       (1<<9)
//////////////////////// Predefined debug levels. /////////////////////////////
/** Test print.
 *
 * This should never be left in application after testing is complete.
 */
#define ODTEST               0
#define ODCRITICAL           0

/** Status print. 
 * 
 * Very important prints, which will very shortly describe the program
 * execution status. These are left to program permanently, and are
 * displayed by default without any debug flags set. 
 *
 * Consider example message:
 * 'Waiting for new teleoperator...'
 */
#define ODSTATUS             1

/// Same level as Status, but for different purpose: Error.
#define ODERROR              1


/** Info print.
 *
 * Quite important or otherwise useful prints. These are used to track
 * what is really happening in the system. Do not confuse this on
 * debug, its just 'a little more' information about the work in
 * progress.
 *
 * Consider examples:
 * 'Teleoperator connected from GIMnet address 0x0001002A'
 * 'Current teleoperator disconnected due to command timeout'
 */
#define ODINFO               2

/// Same level as Info, but for different purpose: Warning.
#define ODWARN               2


/** Verbose info print.
 *
 * Verbose information. Is not critical for understanding what is
 * happening in the software, but can be displayed to find out details
 * of some operations for checking that everything is going as
 * operator intended.
 *
 * Consider examples:
 * 'Using name 'foo' as ownName for connecting to GIMnet'
 * 'Saving to file disabled by cmdline parameter, nothing will be saved to disk during execution'
 */
#define ODVINFO              3

/// Same level as VInfo, but for different purpose: lower level
/// notifications.
#define ODNOTIFY             3


/** Maco for internal debug levels.
 *
 * This macro creates 'debugging' prints. These prints are numbered
 * from 10 upwards according to macro parameter.
 *
 * @param[in] odp               Increment this number to number 10.
 */
#define ODDEBUG(odp)           (10+(odp))
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////


// Other values (as macros, should be enums...)
#define OWNDEBUG_OK                           0
#define OWNDEBUG_ERROR                        -1

#define OWNDEBUG_MAXMOD                       16 // Max modules = 32 * 16


// Constants for colors
#define OWNDEBUG_COLOR_ATTR_RESET           0
#define ODC_RESET                           0

#define OWNDEBUG_COLOR_ATTR_BRIGHT          1
#define ODC_BRIGHT                          1

#define OWNDEBUG_COLOR_ATTR_DIM             2
#define ODC_DIM                             2

#define OWNDEBUG_COLOR_ATTR_UNDERLINE       3
#define ODC_UNDERLINE                       3

#define OWNDEBUG_COLOR_ATTR_BLINK           4
#define ODC_BLINK                           4

#define OWNDEBUG_COLOR_ATTR_REVERSE         7
#define ODC_REVERSE                         7

#define OWNDEBUG_COLOR_ATTR_HIDDEN          8
#define ODC_HIDDEN                          8

#define OWNDEBUG_COLOR_BLACK                0
#define ODC_BLACK                           0

#define OWNDEBUG_COLOR_RED                  1
#define ODC_RED                             1

#define OWNDEBUG_COLOR_GREEN                2
#define ODC_GREEN                           2

#define OWNDEBUG_COLOR_YELLOW               3
#define ODC_YELLOW                          3

#define OWNDEBUG_COLOR_BLUE                 4
#define ODC_BLUE                            4

#define OWNDEBUG_COLOR_MAGENTA              5
#define ODC_MAGENTA                         5

#define OWNDEBUG_COLOR_CYAN                 6
#define ODC_CYAN                            6

#define OWNDEBUG_COLOR_WHITE                7
#define ODC_WHITE                           7


/** dPrint macro with coloring.
 *
 * This macro takes color parameters too. For example;
 * \code
 * dPrintLC(1,OWNDEBUG_COLOR_ATTR_BRIGHT,OWNDEBUG_COLOR_RED, OWNDEBUG_COLOR_BLACK, "Warning");
 * \endcode
 *
 * Prints text warning in bright red on black bacground. You are
 * advised to use some additional macros/wrapper function for calling
 * these with appropriate colors, as specifying the colors on each
 * line can be painful.
 *
 * @param[in] lvl               Level of message
 * @param[in] cattr             Color attribute; select one of group: OWNDEBUG_COLOR_ATTR_*
 * @param[in] cfg               Foreground color; select one of OWNDEBUG_COLOR_*
 * @param[in] cbg               Background color; select one of OWNDEBUG_COLOR_*
 * @param[in] args              printf() format message & variadic arguments
 */
#define dPrintLC( lvl, cattr, cfg, cbg, args... ) debugPrint(0, lvl, 0, (cattr), (cfg), (cbg), __FILE__, __FUNCTION__, __LINE__, ##args)

/// dPrint with level, direct Red On Black.
#define dPrintLCRed( lvl, args... ) debugPrint(0, lvl, 0, (ODC_BRIGHT), (ODC_RED), (ODC_BLACK), __FILE__, __FUNCTION__, __LINE__, ##args)

/// dPrint with level, direct Yellow on black.
#define dPrintLCYellow( lvl, args... ) debugPrint(0, lvl, 0, (ODC_BRIGHT), (ODC_YELLOW), (ODC_BLACK), __FILE__, __FUNCTION__, __LINE__, ##args)

/// dPrint with level, direct Green on black.
#define dPrintLCGreen( lvl, args... ) debugPrint(0, lvl, 0, (ODC_BRIGHT), (ODC_GREEN), (ODC_BLACK), __FILE__, __FUNCTION__, __LINE__, ##args)

/// dPrint with level, direct Blue on black.
#define dPrintLCBlue( lvl, args... ) debugPrint(0, lvl, 0, (ODC_BRIGHT), (ODC_BLUE), (ODC_BLACK), __FILE__, __FUNCTION__, __LINE__, ##args)


/**
 * dPrint is a macro to use when user wants to specify level and message.
 * @param[in] lvl               Level of message
 * @param[in] args              printf() format message & variadic arguments
 */
#define dPrint( lvl, args... ) debugPrint(0, lvl, 0, 0,0,0,__FILE__, __FUNCTION__, __LINE__, ##args);
#define dPrintG dPrint
#define dPrintL dPrint


/**
 * dPrint is a macro to use when user wants to specify level and message, and also display
 * the current value of 'errno' (though it won't  be displayed if it is zero)
 * @param[in] lvl               Level of message
 * @param[in] args              printf() format message & variadic arguments
 */
#define dPrintLE( lvl, args... ) debugPrint(0, lvl, errno,  0,0,0,__FILE__, __FUNCTION__, __LINE__, ##args)


/**
 * dPrintM is a macro to use when a user wants to specify module,
 * level and message.  If you want to use the modulenumbered approach,
 * call macro OWNDEBUG_INITIALIZE_MODULENUMBER() or
 * OWNDEBUG_INITIALIZE_MODULENUMBER_BY_NAME(<some name>) somewhere (in
 * global space) before the first call to dPrintM.
 *
 * @param[in] lvl               Level of message
 * @param[in] args              printf() format message & variadic arguments
 */
#define dPrintM( lvl, args... ) debugPrint(owndebug_current_module_number, lvl, 0,  0,0,0,__FILE__, __FUNCTION__, __LINE__, ##args)


/**
 * dPrintM is a macro to use when a user wants to specify module, level and message,
 * and also display the current value of 'errno' 
 * (though it won't  be displayed if it is zero)
 *
 * @param[in] lvl               Level of message
 * @param[in] args              printf() format message & variadic arguments
 */
#define dPrintME( lvl, args... ) debugPrint(owndebug_current_module_number, lvl, errno,  0,0,0,__FILE__, __FUNCTION__, __LINE__, ##args)


/** Initialize the module for debugging through Module-aware facility.
 *
 * Use this macro only once in a IMPLEMENTATION file (.cpp, .c, etc), and after that you
 * can use the dPrintM... family of functions. This macro automatically uses 
 * the current filename as module name. (Through __FILE__)
 *
 * \see debugDisableModulePrint
 * \see debugEnableModulePrint
 */
#define OWNDEBUG_INITIALIZE_MODULENUMBER() static const int owndebug_current_module_number = owndebugGetModuleBitByName(__FILE__)


/** Initialize the module for debugging through Module-aware facility.
 *
 * Use this macro only once in a IMPLEMENTATION file (.cpp, .c, etc), and after that you
 * can use the dPrintM... family of functions. This macro requires a parameter
 * for defining the module name.
 * 
 * @param[in] mname string to use as a module bit key. The key
 *                  generation stops at first '.', so avoid those to be on the safe
 *                  side.
 *
 * \see debugDisableModulePrint
 * \see debugEnableModulePrint
 */
#define OWNDEBUG_INITIALIZE_MODULENUMBER_BY_NAME(mname) static const int owndebug_current_module_number = owndebugGetModuleBitByName(mname)
  ///////////////////////////////////////////////////////////////////////////////


#ifdef __cplusplus
extern "C" {
#endif
  
  ///////////////////////////////////////////////////////////////////////////////
  // Private function.
  unsigned int owndebugGetModuleBitByName(const char *modulename);
  ///////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////
  

  
  /**
   * Initialize the Debug facility. This must be called before using the debug library!
   * 
   * @return                    OWNDEBUG_OK on success,
   *                            OWNDEBUG_ERROR on any error.
   */
  int debugInit();


  /**
   * Deinitializes the Debug facility, closes
   * logfile (if open)
   *
   */
  void debugDeinit();
  

  /**
   * Sets mode bits. See 'DEBUG_MODE_*' macros. Different TYPE of flags can
   * be combined with binary or.
   *
   */
  void debugSetMode(int mode);


  /**
   * Adds mode bits. See 'DEBUG_MODE_*' macros. Different TYPE of flags can
   * be combined.
   */
  void debugAddMode(int mode);

  
  /**
   * Removes mode bits. See 'DEBUG_MODE_*' macros. Different TYPE of flags can
   * be combined.
   */
  void debugRemoveMode(int mode);


  /**
   * Enables/Disable specified modules debug. (both; log & print)
   *
   * @param[in] bit             Bit number (== module number) If this is 
   *                            negative, ALL available bits are affected. (So,
   *                            allows enabling/disabling ALL)
   * @param[in] value           Zero to Disable, nonzero to Enable.
   *
   */
  void debugSetModuleBit(int bit, int value);


  /** Enabled module debugging by module name.
   *
   * NOTE: For this to work, the module must use the dPrintM family of debugging macros.
   * 
   * @param[in] module          Module name to enable disable. Can be specified without extension.
   *                            if this parameter is NULL, ALL available bits are affected.
   */
  void debugEnableModulePrint(const char *module);


  /** Disable module debugging by module name.
   *
   * NOTE: For this to work, the module must use the dPrintM family of debugging macros.
   * 
   * @param[in] module          Module name to enable disable. Can be specified without extension.
   *                            if this parameter is NULL, ALL available bits are affected.
   */
  void debugDisableModulePrint(const char *module);


  /**
   * Set global debug level to 'lvl'. All prints which have SMALLER OR EQUAL
   * level will be printed. This affects both; 'log level' and 'print level'.
   *
   * @param[in] lvl             Level limit to set for 'log' and 'print'. The value
   *                            must be >= 0
   *                            
   */
  void debugSetGlobalDebugLvl(int lvl);


  /**
   * Set 'logfilename' as target file for logging. The file is openened by this
   * call in 'WRITE' mode (existing files will be truncated). If parameter is
   * NULL, the logging to file will be disabled.
   *
   * @param[in] logfilename     Logfile to open for writing.
   *
   * @return                    OWNDEBUG_OK on success,
   *                            OWNDEBUG_ERROR on any error.
   */
  int debugSetLogFilename(const char *logfilename);


  /**
   * Set the Print stream to 'fname'.
   * 
   * @param[in] fname          'Print' output stream
   * 
   */
  void debugSetPrintFILE(FILE *fname);


  /**
   * Set the Log stream to 'fname'.
   * 
   * @param[in] fname          'Log' output stream
   * 
   */
  void debugSetLogFILE(FILE *fname);


  /**
   * Set the Debug limit level for Print stream. All prints SMALLER
   * or EQUAL to 'lvl' are printed.
   *
   * @param[in] lvl             Level limit for 'Print' stream.
   *
   */
  void debugSetPrintLevel(int lvl);


  /**
   * Set the Debug limit level for Print stream. All prints SMALLER
   * or EQUAL to 'lvl' are printed.
   *
   * @param[in] lvl             Level limit for 'Print' stream.
   *
   */
  void debugSetLogLevel(int lvl);


  /** 
   * Get the PrintLevel of OwnDebug module.
   * 
   * @return                    Returns the current Print level.
   */
  int debugGetPrintLevel(void);



  /** Return the print stream used by the Debug facility.
   */
  FILE *debugGetPrintStream(void);


  /** Return the log stream used by the Debug facility.
   */
  FILE *debugGetLogStream(void);


  /** 
   * Get the LogLevel of OwnDebug module.
   * 
   * @return                    Returns the current Log level.
   */
  int debugGetLogLevel(void);


  /**
   * Debug Print function. Use this through the macros instead.
   * 
   * @param[in] modulebitn      Module number to use for filtering.
   *                            Ignored if Zero.
   * @param[in] lvl             Level of message.
   * @param[in] err             Optional 'errno' value. If this is non-zero,
   *                            verbose errormessage is appended (by strerror)
   * @param[in] color_attr      Color attribute. 0 to disable. See OWNDEBUG_COLOR_ATTR_*
   * @param[in] color_fg        Foreground color. See OWNDEBUG_COLOR_*
   * @param[in] color_bg        Background color. See OWNDEBUG_COLOR_*
   * @param[in] file            Source file
   * @param[in] func            Source function
   * @param[in] line            Source line
   * @param[in] msg             Message format (in printf() format), variadic
   *                            parameters likewise.
   */
  void debugPrint(int modulebitn, int lvl, int err,
                  unsigned char color_attr, unsigned char color_fg, unsigned char color_bg,
		  const char *file, const char *func,
		  int line, const char *msg, ...);

#ifdef __cplusplus
}
#endif

// Debug print stuff.
#ifdef MSVC
// You MUST use this workaround if using MS Visual C++ Compiler.
// (Doesn't understand the '...' suffix in macro (?)
// This is not perfect. FOr example, the '\n' must be in beginning
// of the line
#define dPrint printf("\n%s:%s; ",__FILE__,__FUNCTION__);printf
#endif //MSCV                             
#endif //_OWNDEBUG_H_

/******************************************************************************
 * 
 * $Log: not supported by cvs2svn $
 * Revision 1.23  2009-05-13 07:18:25  amaula
 * Doxygens file tag parameter removed, as no parameter means 'document this file'
 *
 * Revision 1.22  2009-05-05 08:23:31  amaula
 * Macros added too.
 *
 * Revision 1.21  2009-04-28 10:12:25  amaula
 * Ups.
 *
 * Revision 1.20  2009-04-07 07:58:12  amaula
 * Change reverted.
 *
 * Revision 1.18  2009-02-10 11:40:27  amaula
 * More levels defined.
 *
 * Revision 1.17  2009-02-09 10:21:08  amaula
 * Some documentation added.
 *
 * Revision 1.16  2008-12-17 08:13:48  amaula
 * Added a few predefined debuglevels, and documented the dPrintLC macro.
 *
 * Revision 1.15  2008-10-13 11:34:30  amaula
 * Fixed 'msg' parameter of debugPrint to be const, as it should. Should remove A LOT of warnings.
 *
 * Revision 1.14  2008-09-16 06:51:10  amaula
 * Color support enhanced. Now integrated to debugPrint function. Now 9 parameters for function call :)
 *
 * Revision 1.13  2008-09-15 13:03:29  amaula
 * Simple color support added. A little thread-unsafe, but who cares :)
 *
 * Revision 1.12  2008-05-01 23:59:12  amaula
 * Added functions for getting the FILE streams used by the library.
 *
 * Revision 1.11  2008-03-12 12:09:58  amaula
 * Finalized module-selection interface. Now you can globally disable some printing modules prints despite its printing levels.
 *
 * Revision 1.10  2008-03-07 12:17:15  amaula
 * Added method for reading the current loglevel
 *
 * Revision 1.9  2008-02-13 12:20:59  amaula
 * Documentation updates.
 *
 * Revision 1.8  2007-08-09 10:30:58  amaula
 * Added possibility to display THREAD ID. Not very readable though :(
 *
 * Revision 1.7  2007/03/15 08:52:44  amaula
 * Debug printing updated - Print filter by Module should now work.
 *
 * Revision 1.6  2007/03/05 13:07:00  amaula
 * Added new features for format selection.
 *
 * Revision 1.5  2007/03/05 12:17:12  amaula
 * Added library version define.
 *
 * Revision 1.4  2007/03/05 10:07:54  amaula
 * Added immediate flush support for all print operations
 *
 * Revision 1.3  2007/01/04 14:33:23  amaula
 * New prototypes and documentation added
 *
 * Revision 1.2  2006/09/25 12:19:08  amaula
 * Wtf fix
 *
 * Revision 1.1  2006/09/25 09:50:36  amaula
 * OwnUtils splitted to parts
 *
 *
 *****************************************************************************/

